
#include "lib_alsa.h"


//#define  CONFIG_NOT_USED_ALSA


typedef enum
{
    ALSA_RUN_INIT = 0,
		
    ALSA_RUN_CONFIG,
    
    ALSA_RUN_PLAYING,

    ALSA_RUN_STOP,

	ALSA_RUN_IDLE,

} alsa_run_state_e;
	

#define ADJUST_VOLUME(s, v) (s = (s*v)/SDL_MIX_MAXVOLUME)
#define ADJUST_VOLUME_U8(s, v)  (s = (((s-128)*v)/SDL_MIX_MAXVOLUME)+128)

static const Uint8 mix8[] = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03,
    0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
    0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
    0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24,
    0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A,
    0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
    0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
    0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B,
    0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
    0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71,
    0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C,
    0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
    0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92,
    0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D,
    0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8,
    0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3,
    0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE,
    0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9,
    0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4,
    0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
    0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA,
    0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5,
    0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFE, 0xFE,
    0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
    0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
    0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
    0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
    0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
    0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
    0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
    0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
    0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
    0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
    0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
    0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE
};

static pthread_t  pAlsa_play_thread;
static snd_pcm_t *playback_handle   = NULL;
static int 		  alsa_write_size	= 0;
static int 		  alsa_run_state 	= ALSA_RUN_INIT;

static uint8_t *  packet_data  		= NULL;
static int 	      packet_size  		= 0;
static int64_t    packet_pts 	 	= 0;
static uint8_t *  packet_p     		= NULL;


#define ALSA_HCTL_NAME       "default"
#define ALSA_HCTL_ELEM_PATH  "name='Playback Path'"
#define ALSA_HCTL_ELEM_NAME  "name='Playback Volume'"
 

int alsa_config_playback_path(int volume)//0-6
{
	int ret  = -1;
	long l_v = 0;
	long r_v = 0;

	snd_hctl_t *pt_hctl=NULL;
	snd_hctl_elem_t *pt_helem=NULL;

	snd_ctl_elem_id_t *pt_id =NULL;
	snd_ctl_elem_value_t *pt_value =NULL;

	snd_ctl_elem_id_alloca(&pt_id);

	//1. open hctl
	if(snd_hctl_open(&pt_hctl, ALSA_HCTL_NAME, 0)<0)
	{
		printf("snd_hctl_open error.\n");
		return -1;
	}

	//2. load hctl
	if(snd_hctl_load(pt_hctl)<0)
	{
		printf("snd_hctl_load error.\n");
		if(pt_hctl){
			snd_hctl_close(pt_hctl);
			pt_hctl = NULL;
		}
		return -1;
	}

	//3. parse ctl elem
	if(snd_ctl_ascii_elem_id_parse(pt_id, ALSA_HCTL_ELEM_PATH)<0)
	{
		printf("snd_ctl_ascii_elem_id_parse error.\n"); 
		if(pt_hctl){
			snd_hctl_close(pt_hctl);
			pt_hctl = NULL;
		}
		return -1;
	}

	//4. find hctl elem
	pt_helem = snd_hctl_find_elem(pt_hctl, pt_id);
	if (!pt_helem)
	{
		printf("snd_hctl_find_elem error.\n");
		if(pt_hctl){
			snd_hctl_close(pt_hctl);
			pt_hctl = NULL;
		}
		return -1;
	}

	snd_ctl_elem_value_alloca(&pt_value);

	//5. read hctl elem
	if(snd_hctl_elem_read(pt_helem, pt_value)<0)
	{
		printf("snd_hctl_elem_read error.\n");
		if(pt_hctl){
			snd_hctl_close(pt_hctl);
			pt_hctl = NULL;
		}
		return -1;
	}

	l_v =snd_ctl_elem_value_get_integer(pt_value, 0);
	r_v =snd_ctl_elem_value_get_integer(pt_value, 1);

	//set elem value
	snd_ctl_elem_value_set_integer(pt_value, 0, volume);

	//6 write hctl elem
	if(snd_hctl_elem_write(pt_helem, pt_value))
	{
		printf("snd_hctl_elem_write error.\n"); 
		if(pt_hctl){
			snd_hctl_close(pt_hctl);
			pt_hctl = NULL;
		}
		return -1;
	}

	//snd_ctl_elem_value_free(pt_value);

	if(pt_hctl){
		snd_hctl_close(pt_hctl);
		pt_hctl = NULL;
	}

	return 0;
}


int alsa_config_playback_volume(int volume)//0-100
{
	int ret = -1;
	long l_volume = 0;
	long r_volume = 0;
	long * pdata  = NULL;

	snd_hctl_t *pt_hctl=NULL;
	snd_hctl_elem_t *pt_helem=NULL;

	snd_ctl_elem_id_t *pt_id;
	snd_ctl_elem_value_t *pt_value;

	snd_ctl_elem_id_alloca(&pt_id);

	//1. open hctl
	if(snd_hctl_open(&pt_hctl, ALSA_HCTL_NAME, 0)<0)
	{
		printf("snd_hctl_open error.\n");
		return -1;
	}

	//2. load hctl
	if(snd_hctl_load(pt_hctl)<0)
	{
		printf("snd_hctl_load error.\n");
		if(pt_hctl){
			snd_hctl_close(pt_hctl);
			pt_hctl = NULL;
		}
		return -1;
	}

	//3. parse ctl elem
	if(snd_ctl_ascii_elem_id_parse(pt_id, ALSA_HCTL_ELEM_NAME)<0)
	{
		printf("snd_ctl_ascii_elem_id_parse error.\n");
		if(pt_hctl){
			snd_hctl_close(pt_hctl);
			pt_hctl = NULL;
		}
		return -1;
	}

	//4. find hctl elem
	pt_helem = snd_hctl_find_elem(pt_hctl, pt_id);
	if (!pt_helem)
	{
		printf("snd_hctl_find_elem error.\n");
		if(pt_hctl){
			snd_hctl_close(pt_hctl);
			pt_hctl = NULL;
		}
		return -1;
	}

	snd_ctl_elem_value_alloca(&pt_value);
	//5. read hctl elem
	if(snd_hctl_elem_read(pt_helem, pt_value)<0)
	{
		printf("snd_hctl_elem_read error.\n");
		if(pt_hctl){
			snd_hctl_close(pt_hctl);
			pt_hctl = NULL;
		}
		return -1;
	}

	//get elem value
	l_volume =snd_ctl_elem_value_get_integer(pt_value, 0);
	r_volume =snd_ctl_elem_value_get_integer(pt_value, 1);

	//set elem value
	snd_ctl_elem_value_set_integer(pt_value, 0, volume*255/100);
	snd_ctl_elem_value_set_integer(pt_value, 1, volume*255/100);

	//6 write hctl elem
	if(snd_hctl_elem_write(pt_helem, pt_value))
	{
		printf("snd_hctl_elem_write error.\n");
		if(pt_hctl){
			snd_hctl_close(pt_hctl);
			pt_hctl = NULL;
		}
		return -1;
	}

	pdata=(long *)snd_ctl_elem_value_get_bytes(pt_value);
	//printf("after changing, left volume is:%d,right volume is %d\n",*pdata,*(pdata+1));

	//snd_ctl_elem_value_free(pt_value);

	if(pt_hctl)
	{
		snd_hctl_close(pt_hctl);
		pt_hctl = NULL;
	}

	return 0;
}



static double    alsa_audio_clock 	= 0;

int alsa_audio_sync_clock(int64_t pts)
{

	AVRational  avrational_audio;
	
	if(!pts)
		return -1;

	avrational_audio.num = 0;
	avrational_audio.den = 0;
	
	ffmpeg_get_audio_time_base(&avrational_audio.num,&avrational_audio.den);

	alsa_audio_clock = av_q2d(avrational_audio)*pts;

	//log_d("audio clock = %f,pts=%lld\n",alsa_audio_clock,pts);

	return 0;

}

int alsa_get_audio_clock(double * clock)
{
	*clock = alsa_audio_clock;

	return 0;
}


int alsa_pcm_buff_clear(void)
{
	int ret  = -1;
	
	if(!playback_handle)
		return -1;

	ret = snd_pcm_drop(playback_handle);
	if(ret < 0){
		log_d("snd_pcm_drop,ret=%d,%s\n",ret,snd_strerror(ret));
		return -1;
	}

	ret = snd_pcm_prepare(playback_handle);
	if(ret < 0){
		log_d("snd_pcm_prepare,ret=%d,%s\n",ret,snd_strerror(ret));
		return -1;
	}

	return 0;
}

int alsa_pcm_data_clear(void)
{
	packet_size = 0;


	return 0;
}


int alsa_pcm_play(void)
{
	int ret  = -1;
	
	long      us_start    = 0;
	long      us_end      = 0;
	
	if(!playback_handle)
		return -1;

	if((packet_size <= 0)||(packet_size < alsa_write_size*4))
	{
		log_d("\n");
		ret = queue_audio_pcm_read(&packet_data,&packet_size,&packet_pts);
		if(ret < 0){		
			packet_size = 0;
			packet_data = NULL;
			return -1;
		}
		//queue_audio_pcm_free();
		packet_p = packet_data;
		log_d("\n");
	}

	
	log_d("packet_p=%x\n",packet_p);
	log_d("packet_size=%d\n",packet_size);
	us_start = platform_get_microsecond();
	ret = snd_pcm_writei(playback_handle, packet_p, alsa_write_size);
	if(ret < 0){
		log_d("snd pcm write error,ret=%d,%s\n",ret,snd_strerror(ret));
		ret = snd_pcm_recover(playback_handle,ret,0);
		//ret = snd_pcm_prepare(playback_handle);
		if(ret < 0){
			log_d("snd pcm prepare error,ret=%d,%s\n",ret,snd_strerror(ret));
			return -1;
		}

		ret = snd_pcm_writei(playback_handle, packet_p, alsa_write_size);
		if(ret < 0){
			log_d("snd pcm write error,ret=%d,%s\n",ret,snd_strerror(ret));
		}	
	}
	log_d("\n");
    packet_p    = packet_p    + alsa_write_size*4; //16位 双声道
	packet_size = packet_size - alsa_write_size*4;	
	
	log_d("\n");
	us_end = platform_get_microsecond();
	
	//log_d("play pcm time = %d\n",us_end - us_start);
	
	return 0;

}



int alsa_device_config(void)
{
	int ret = -1;
	int sample_rate = 0;
	int channels    = 0;
	int sample_fmt  = 0;

	snd_pcm_hw_params_t *hw_params = NULL;

	if(!playback_handle)
		return -1;

	//如果需要非阻塞方式，则将第二个参数，设置为1。
	//snd_pcm_open后，默认为阻塞方式，因此，该接口可以不用调用。

	//snd_pcm_nonblock(playback_handle,1);

	ret = snd_pcm_hw_params_malloc(&hw_params);
    if(ret < 0){
        log_e("snd_pcm_hw_params_malloc error\n");
		
		if(hw_params){
			snd_pcm_hw_params_free(hw_params);
		}
        return -1;
    }

	ret = snd_pcm_hw_params_any(playback_handle, hw_params);
    if(ret < 0){
        log_e("snd_pcm_hw_params_any error\n");		
		if(hw_params){
			snd_pcm_hw_params_free(hw_params);
		}
        return -1;
    }
	
	ret = snd_pcm_hw_params_set_access(playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
    if(ret < 0){
        log_e("snd_pcm_hw_params_any error\n");
		if(hw_params){
			snd_pcm_hw_params_free(hw_params);
		}
        return -1;
    }
	
	sample_fmt = ffmpeg_get_sample_fmt();

	if(sample_fmt == 8)
	{
		sample_fmt = SND_PCM_FORMAT_S8;
	}
	
	if(sample_fmt == 16)
	{
		sample_fmt = SND_PCM_FORMAT_S16_LE;
	}

	ret = snd_pcm_hw_params_set_format (playback_handle, hw_params, SND_PCM_FORMAT_S16_LE);
    if(ret < 0){
        log_e("snd_pcm_hw_params_set_format error\n");
		if(hw_params){
			snd_pcm_hw_params_free(hw_params);
		}
        return -1;
    }
	int dir    = 0;
	sample_rate = ffmpeg_get_sample_rate();
	
	log_d("sample_rate=%d\n",sample_rate);
	
	ret = snd_pcm_hw_params_set_rate_near(playback_handle, hw_params, &sample_rate, &dir);
    if(ret < 0){
        log_e("snd_pcm_hw_params_set_rate_near error\n");
		if(hw_params){
			snd_pcm_hw_params_free(hw_params);
		}
        return -1;
    }

	channels = ffmpeg_get_channels();

	log_d("channels=%d\n",channels);

	ret = snd_pcm_hw_params_set_channels(playback_handle, hw_params, channels);
    if(ret < 0){
        log_e("snd_pcm_hw_params_set_channels error\n");
		if(hw_params){
			snd_pcm_hw_params_free(hw_params);
		}
        return -1;
    }
	
	int frames = 32;

	//snd_pcm_hw_params_set_period_size(playback_handle,hw_params, 1024, 0);

	ret = snd_pcm_hw_params(playback_handle, hw_params);
    if(ret < 0){
        log_e("snd_pcm_hw_params error\n");
		if(hw_params){
			snd_pcm_hw_params_free(hw_params);
		}
        return -1;
    }
	

    snd_pcm_hw_params_get_period_size(hw_params, &alsa_write_size, 0);
    snd_pcm_hw_params_free(hw_params);

	log_d("alsa_write_size=%d\n",alsa_write_size);

	ret = snd_pcm_prepare(playback_handle);
    if(ret < 0){
        log_e("snd_pcm_prepare error\n");
        return -1;
    }

	
	log_d("alsa config device succeed.\n");

	return 0;
}


void* alsa_pcm_play_thread(void *arg)
{

	while(1)
	{		
		usleep(10);
		if(!ffmpeg_get_decode_flag()){
			continue;
		}

		if(!ffmpeg_packet_is_ready()){
			continue;
		}

		switch(alsa_run_state){
		case ALSA_RUN_INIT:
		{
			alsa_run_state = ALSA_RUN_CONFIG;
			break;
		}
		case ALSA_RUN_CONFIG:
		{
			alsa_device_config();
			alsa_config_playback_path(6);
			alsa_config_playback_volume(70);
			alsa_run_state = ALSA_RUN_PLAYING;
			break;
		}	
		case ALSA_RUN_PLAYING:
		{
			alsa_pcm_play();

			break;
		}
		case ALSA_RUN_STOP:
		{

			break;
		}	
		case ALSA_RUN_IDLE:
		{

			break;
		}				
		default:break;			
		}
	}
}


int lib_alsa_init(void)
{
	int ret = -1;
	//snd_pcm_info_t info;

#ifdef CONFIG_NOT_USED_ALSA

	return 0;

#endif

	log_d("lib alsa init.\n");

	#if 1
	//hw:0,0 //rockchiprk809
	//hw:1,0 //rockchiphdmi
	
	ret = snd_pcm_open(&playback_handle, "hw:1,0", SND_PCM_STREAM_PLAYBACK, 0);
	//ret = snd_pcm_open(&playback_handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
    if(ret < 0){
        log_e("snd_pcm_open error\n");
        return -1;
    }

	//snd_pcm_info(playback_handle, &info);


	ret=pthread_create(&pAlsa_play_thread,NULL,alsa_pcm_play_thread,NULL);
	if(ret != 0){
		log_e("thread error=%s\n",strerror(ret));
	}
	

	log_i("alsa open device succeed.\n");
	
	#endif

	return 0;
}


int lib_alsa_deinit(void)
{

#ifdef CONFIG_NOT_USED_ALSA

	return 0;

#endif


	log_d("lib alsa exit.\n");

	if(playback_handle){
		snd_pcm_close(playback_handle);
		playback_handle = NULL;
	}
    	




	return 0;
}
